/*!
  * vue-router v4.0.10
  * (c) 2021 Eduardo San Martin Morote
  * @license MIT
  */
var VueRouter = (function (exports, vue) {
  'use strict';

  const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
  const PolySymbol = (name) =>
      // vr = vue router
      hasSymbol
          ? Symbol('[vue-router]: ' + name )
          : ('[vue-router]: ' ) + name;
  // rvlm = Router View Location Matched
  /**
   * RouteRecord being rendered by the closest ancestor Router View. Used for
   * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
   * Location Matched
   *
   * @internal
   */
  const matchedRouteKey = /*#__PURE__*/ PolySymbol('router view location matched' );
  /**
   * Allows overriding the router view depth to control which component in
   * `matched` is rendered. rvd stands for Router View Depth
   *
   * @internal
   */
  const viewDepthKey = /*#__PURE__*/ PolySymbol('router view depth' );
  /**
   * Allows overriding the router instance returned by `useRouter` in tests. r
   * stands for router
   *
   * @internal
   */
  const routerKey = /*#__PURE__*/ PolySymbol('router' );
  /**
   * Allows overriding the current route returned by `useRoute` in tests. rl
   * stands for route location
   *
   * @internal
   */
  const routeLocationKey = /*#__PURE__*/ PolySymbol('route location' );
  /**
   * Allows overriding the current route used by router-view. Internally this is
   * used when the `route` prop is passed.
   *
   * @internal
   */
  const routerViewLocationKey = /*#__PURE__*/ PolySymbol('router view location' );

  const isBrowser = typeof window !== 'undefined';

  function isESModule(obj) {
    return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
  }
  const assign = Object.assign;
  function applyToParams(fn, params) {
    const newParams = {};
    for (const key in params) {
      const value = params[key];
      newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
    }
    return newParams;
  }
  let noop = () => { };

  function warn(msg) {
    // avoid using ...args as it breaks in older Edge builds
    const args = Array.from(arguments).slice(1);
    console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args));
  }

  const TRAILING_SLASH_RE = /\/$/;
  const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
  /**
   * Transforms an URI into a normalized history location
   *
   * @param parseQuery
   * @param location - URI to normalize
   * @param currentLocation - current absolute location. Allows resolving relative
   * paths. Must start with `/`. Defaults to `/`
   * @returns a normalized history location
   */
  function parseURL(parseQuery, location, currentLocation = '/') {
    let path, query = {}, searchString = '', hash = '';
    // Could use URL and URLSearchParams but IE 11 doesn't support it
    const searchPos = location.indexOf('?');
    const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0);
    if (searchPos > -1) {
      path = location.slice(0, searchPos);
      searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
      query = parseQuery(searchString);
    }
    if (hashPos > -1) {
      path = path || location.slice(0, hashPos);
      // keep the # character
      hash = location.slice(hashPos, location.length);
    }
    // no search and no query
    path = resolveRelativePath(path != null ? path : location, currentLocation);
    // empty path means a relative query or hash `?foo=f`, `#thing`
    return {
      fullPath: path + (searchString && '?') + searchString + hash,
      path,
      query,
      hash,
    };
  }
  /**
   * Stringifies a URL object
   *
   * @param stringifyQuery
   * @param location
   */
  function stringifyURL(stringifyQuery, location) {
    let query = location.query ? stringifyQuery(location.query) : '';
    return location.path + (query && '?') + query + (location.hash || '');
  }
  /**
   * Strips off the base from the beginning of a location.pathname in a non
   * case-sensitive way.
   *
   * @param pathname - location.pathname
   * @param base - base to strip off
   */
  function stripBase(pathname, base) {
    // no base or base is not found at the beginning
    if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
      return pathname;
    return pathname.slice(base.length) || '/';
  }
  /**
   * Checks if two RouteLocation are equal. This means that both locations are
   * pointing towards the same {@link RouteRecord} and that all `params`, `query`
   * parameters and `hash` are the same
   *
   * @param a - first {@link RouteLocation}
   * @param b - second {@link RouteLocation}
   */
  function isSameRouteLocation(stringifyQuery, a, b) {
    let aLastIndex = a.matched.length - 1;
    let bLastIndex = b.matched.length - 1;
    return (aLastIndex > -1 &&
        aLastIndex === bLastIndex &&
        isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) &&
        isSameRouteLocationParams(a.params, b.params) &&
        stringifyQuery(a.query) === stringifyQuery(b.query) &&
        a.hash === b.hash);
  }
  /**
   * Check if two `RouteRecords` are equal. Takes into account aliases: they are
   * considered equal to the `RouteRecord` they are aliasing.
   *
   * @param a - first {@link RouteRecord}
   * @param b - second {@link RouteRecord}
   */
  function isSameRouteRecord(a, b) {
    // since the original record has an undefined value for aliasOf
    // but all aliases point to the original record, this will always compare
    // the original record
    return (a.aliasOf || a) === (b.aliasOf || b);
  }
  function isSameRouteLocationParams(a, b) {
    if (Object.keys(a).length !== Object.keys(b).length)
      return false;
    for (let key in a) {
      if (!isSameRouteLocationParamsValue(a[key], b[key]))
        return false;
    }
    return true;
  }
  function isSameRouteLocationParamsValue(a, b) {
    return Array.isArray(a)
        ? isEquivalentArray(a, b)
        : Array.isArray(b)
            ? isEquivalentArray(b, a)
            : a === b;
  }
  /**
   * Check if two arrays are the same or if an array with one single entry is the
   * same as another primitive value. Used to check query and parameters
   *
   * @param a - array of values
   * @param b - array of values or a single value
   */
  function isEquivalentArray(a, b) {
    return Array.isArray(b)
        ? a.length === b.length && a.every((value, i) => value === b[i])
        : a.length === 1 && a[0] === b;
  }
  /**
   * Resolves a relative path that starts with `.`.
   *
   * @param to - path location we are resolving
   * @param from - currentLocation.path, should start with `/`
   */
  function resolveRelativePath(to, from) {
    if (to.startsWith('/'))
      return to;
    if (!from.startsWith('/')) {
      warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
      return to;
    }
    if (!to)
      return from;
    const fromSegments = from.split('/');
    const toSegments = to.split('/');
    let position = fromSegments.length - 1;
    let toPosition;
    let segment;
    for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
      segment = toSegments[toPosition];
      // can't go below zero
      if (position === 1 || segment === '.')
        continue;
      if (segment === '..')
        position--;
      // found something that is not relative path
      else
        break;
    }
    return (fromSegments.slice(0, position).join('/') +
        '/' +
        toSegments
            .slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
            .join('/'));
  }

  var NavigationType;
  (function (NavigationType) {
    NavigationType["pop"] = "pop";
    NavigationType["push"] = "push";
  })(NavigationType || (NavigationType = {}));
  var NavigationDirection;
  (function (NavigationDirection) {
    NavigationDirection["back"] = "back";
    NavigationDirection["forward"] = "forward";
    NavigationDirection["unknown"] = "";
  })(NavigationDirection || (NavigationDirection = {}));
  /**
   * Starting location for Histories
   */
  const START = '';
  // Generic utils
  /**
   * Normalizes a base by removing any trailing slash and reading the base tag if
   * present.
   *
   * @param base - base to normalize
   */
  function normalizeBase(base) {
    if (!base) {
      if (isBrowser) {
        // respect <base> tag
        const baseEl = document.querySelector('base');
        base = (baseEl && baseEl.getAttribute('href')) || '/';
        // strip full URL origin
        base = base.replace(/^\w+:\/\/[^\/]+/, '');
      }
      else {
        base = '/';
      }
    }
    // ensure leading slash when it was removed by the regex above avoid leading
    // slash with hash because the file could be read from the disk like file://
    // and the leading slash would cause problems
    if (base[0] !== '/' && base[0] !== '#')
      base = '/' + base;
    // remove the trailing slash so all other method can just do `base + fullPath`
    // to build an href
    return removeTrailingSlash(base);
  }
  // remove any character before the hash
  const BEFORE_HASH_RE = /^[^#]+#/;
  function createHref(base, location) {
    return base.replace(BEFORE_HASH_RE, '#') + location;
  }

  function getElementPosition(el, offset) {
    const docRect = document.documentElement.getBoundingClientRect();
    const elRect = el.getBoundingClientRect();
    return {
      behavior: offset.behavior,
      left: elRect.left - docRect.left - (offset.left || 0),
      top: elRect.top - docRect.top - (offset.top || 0),
    };
  }
  const computeScrollPosition = () => ({
    left: window.pageXOffset,
    top: window.pageYOffset,
  });
  function scrollToPosition(position) {
    let scrollToOptions;
    if ('el' in position) {
      let positionEl = position.el;
      const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#');
      /**
       * `id`s can accept pretty much any characters, including CSS combinators
       * like `>` or `~`. It's still possible to retrieve elements using
       * `document.getElementById('~')` but it needs to be escaped when using
       * `document.querySelector('#\\~')` for it to be valid. The only
       * requirements for `id`s are them to be unique on the page and to not be
       * empty (`id=""`). Because of that, when passing an id selector, it should
       * be properly escaped for it to work with `querySelector`. We could check
       * for the id selector to be simple (no CSS combinators `+ >~`) but that
       * would make things inconsistent since they are valid characters for an
       * `id` but would need to be escaped when using `querySelector`, breaking
       * their usage and ending up in no selector returned. Selectors need to be
       * escaped:
       *
       * - `#1-thing` becomes `#\31 -thing`
       * - `#with~symbols` becomes `#with\\~symbols`
       *
       * - More information about  the topic can be found at
       *   https://mathiasbynens.be/notes/html5-id-class.
       * - Practical example: https://mathiasbynens.be/demo/html5-id
       */
      if (typeof position.el === 'string') {
        if (!isIdSelector || !document.getElementById(position.el.slice(1))) {
          try {
            let foundEl = document.querySelector(position.el);
            if (isIdSelector && foundEl) {
              warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
              // return to avoid other warnings
              return;
            }
          }
          catch (err) {
            warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
            // return to avoid other warnings
            return;
          }
        }
      }
      const el = typeof positionEl === 'string'
          ? isIdSelector
              ? document.getElementById(positionEl.slice(1))
              : document.querySelector(positionEl)
          : positionEl;
      if (!el) {
        warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
        return;
      }
      scrollToOptions = getElementPosition(el, position);
    }
    else {
      scrollToOptions = position;
    }
    if ('scrollBehavior' in document.documentElement.style)
      window.scrollTo(scrollToOptions);
    else {
      window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset);
    }
  }
  function getScrollKey(path, delta) {
    const position = history.state ? history.state.position - delta : -1;
    return position + path;
  }
  const scrollPositions = new Map();
  function saveScrollPosition(key, scrollPosition) {
    scrollPositions.set(key, scrollPosition);
  }
  function getSavedScrollPosition(key) {
    const scroll = scrollPositions.get(key);
    // consume it so it's not used again
    scrollPositions.delete(key);
    return scroll;
  }
  // TODO: RFC about how to save scroll position
  /**
   * ScrollBehavior instance used by the router to compute and restore the scroll
   * position when navigating.
   */
      // export interface ScrollHandler<ScrollPositionEntry extends HistoryStateValue, ScrollPosition extends ScrollPositionEntry> {
      //   // returns a scroll position that can be saved in history
      //   compute(): ScrollPositionEntry
      //   // can take an extended ScrollPositionEntry
      //   scroll(position: ScrollPosition): void
      // }
      // export const scrollHandler: ScrollHandler<ScrollPosition> = {
      //   compute: computeScroll,
      //   scroll: scrollToPosition,
      // }

  let createBaseLocation = () => location.protocol + '//' + location.host;
  /**
   * Creates a normalized history location from a window.location object
   * @param location -
   */
  function createCurrentLocation(base, location) {
    const { pathname, search, hash } = location;
    // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
    const hashPos = base.indexOf('#');
    if (hashPos > -1) {
      let slicePos = hash.includes(base.slice(hashPos))
          ? base.slice(hashPos).length
          : 1;
      let pathFromHash = hash.slice(slicePos);
      // prepend the starting slash to hash so the url starts with /#
      if (pathFromHash[0] !== '/')
        pathFromHash = '/' + pathFromHash;
      return stripBase(pathFromHash, '');
    }
    const path = stripBase(pathname, base);
    return path + search + hash;
  }
  function useHistoryListeners(base, historyState, currentLocation, replace) {
    let listeners = [];
    let teardowns = [];
    // TODO: should it be a stack? a Dict. Check if the popstate listener
    // can trigger twice
    let pauseState = null;
    const popStateHandler = ({ state, }) => {
      const to = createCurrentLocation(base, location);
      const from = currentLocation.value;
      const fromState = historyState.value;
      let delta = 0;
      if (state) {
        currentLocation.value = to;
        historyState.value = state;
        // ignore the popstate and reset the pauseState
        if (pauseState && pauseState === from) {
          pauseState = null;
          return;
        }
        delta = fromState ? state.position - fromState.position : 0;
      }
      else {
        replace(to);
      }
      // console.log({ deltaFromCurrent })
      // Here we could also revert the navigation by calling history.go(-delta)
      // this listener will have to be adapted to not trigger again and to wait for the url
      // to be updated before triggering the listeners. Some kind of validation function would also
      // need to be passed to the listeners so the navigation can be accepted
      // call all listeners
      listeners.forEach(listener => {
        listener(currentLocation.value, from, {
          delta,
          type: NavigationType.pop,
          direction: delta
              ? delta > 0
                  ? NavigationDirection.forward
                  : NavigationDirection.back
              : NavigationDirection.unknown,
        });
      });
    };
    function pauseListeners() {
      pauseState = currentLocation.value;
    }
    function listen(callback) {
      // setup the listener and prepare teardown callbacks
      listeners.push(callback);
      const teardown = () => {
        const index = listeners.indexOf(callback);
        if (index > -1)
          listeners.splice(index, 1);
      };
      teardowns.push(teardown);
      return teardown;
    }
    function beforeUnloadListener() {
      const { history } = window;
      if (!history.state)
        return;
      history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), '');
    }
    function destroy() {
      for (const teardown of teardowns)
        teardown();
      teardowns = [];
      window.removeEventListener('popstate', popStateHandler);
      window.removeEventListener('beforeunload', beforeUnloadListener);
    }
    // setup the listeners and prepare teardown callbacks
    window.addEventListener('popstate', popStateHandler);
    window.addEventListener('beforeunload', beforeUnloadListener);
    return {
      pauseListeners,
      listen,
      destroy,
    };
  }
  /**
   * Creates a state object
   */
  function buildState(back, current, forward, replaced = false, computeScroll = false) {
    return {
      back,
      current,
      forward,
      replaced,
      position: window.history.length,
      scroll: computeScroll ? computeScrollPosition() : null,
    };
  }
  function useHistoryStateNavigation(base) {
    const { history, location } = window;
    // private variables
    let currentLocation = {
      value: createCurrentLocation(base, location),
    };
    let historyState = { value: history.state };
    // build current history entry as this is a fresh navigation
    if (!historyState.value) {
      changeLocation(currentLocation.value, {
        back: null,
        current: currentLocation.value,
        forward: null,
        // the length is off by one, we need to decrease it
        position: history.length - 1,
        replaced: true,
        // don't add a scroll as the user may have an anchor and we want
        // scrollBehavior to be triggered without a saved position
        scroll: null,
      }, true);
    }
    function changeLocation(to, state, replace) {
      /**
       * if a base tag is provided and we are on a normal domain, we have to
       * respect the provided `base` attribute because pushState() will use it and
       * potentially erase anything before the `#` like at
       * https://github.com/vuejs/vue-router-next/issues/685 where a base of
       * `/folder/#` but a base of `/` would erase the `/folder/` section. If
       * there is no host, the `<base>` tag makes no sense and if there isn't a
       * base tag we can just use everything after the `#`.
       */
      const hashIndex = base.indexOf('#');
      const url = hashIndex > -1
          ? (location.host && document.querySelector('base')
          ? base
          : base.slice(hashIndex)) + to
          : createBaseLocation() + base + to;
      try {
        // BROWSER QUIRK
        // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds
        history[replace ? 'replaceState' : 'pushState'](state, '', url);
        historyState.value = state;
      }
      catch (err) {
        {
          warn('Error with push/replace State', err);
        }
        // Force the navigation, this also resets the call count
        location[replace ? 'replace' : 'assign'](url);
      }
    }
    function replace(to, data) {
      const state = assign({}, history.state, buildState(historyState.value.back,
          // keep back and forward entries but override current position
          to, historyState.value.forward, true), data, { position: historyState.value.position });
      changeLocation(to, state, true);
      currentLocation.value = to;
    }
    function push(to, data) {
      // Add to current entry the information of where we are going
      // as well as saving the current position
      const currentState = assign({},
          // use current history state to gracefully handle a wrong call to
          // history.replaceState
          // https://github.com/vuejs/vue-router-next/issues/366
          historyState.value, history.state, {
            forward: to,
            scroll: computeScrollPosition(),
          });
      if (!history.state) {
        warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` +
            `history.replaceState(history.state, '', url)\n\n` +
            `You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`);
      }
      changeLocation(currentState.current, currentState, true);
      const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
      changeLocation(to, state, false);
      currentLocation.value = to;
    }
    return {
      location: currentLocation,
      state: historyState,
      push,
      replace,
    };
  }
  /**
   * Creates an HTML5 history. Most common history for single page applications.
   *
   * @param base -
   */
  function createWebHistory(base) {
    base = normalizeBase(base);
    const historyNavigation = useHistoryStateNavigation(base);
    const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
    function go(delta, triggerListeners = true) {
      if (!triggerListeners)
        historyListeners.pauseListeners();
      history.go(delta);
    }
    const routerHistory = assign({
      // it's overridden right after
      location: '',
      base,
      go,
      createHref: createHref.bind(null, base),
    }, historyNavigation, historyListeners);
    Object.defineProperty(routerHistory, 'location', {
      enumerable: true,
      get: () => historyNavigation.location.value,
    });
    Object.defineProperty(routerHistory, 'state', {
      enumerable: true,
      get: () => historyNavigation.state.value,
    });
    return routerHistory;
  }

  /**
   * Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
   * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
   *
   * @param base - Base applied to all urls, defaults to '/'
   * @returns a history object that can be passed to the router constructor
   */
  function createMemoryHistory(base = '') {
    let listeners = [];
    let queue = [START];
    let position = 0;
    function setLocation(location) {
      position++;
      if (position === queue.length) {
        // we are at the end, we can simply append a new entry
        queue.push(location);
      }
      else {
        // we are in the middle, we remove everything from here in the queue
        queue.splice(position);
        queue.push(location);
      }
    }
    function triggerListeners(to, from, { direction, delta }) {
      const info = {
        direction,
        delta,
        type: NavigationType.pop,
      };
      for (let callback of listeners) {
        callback(to, from, info);
      }
    }
    const routerHistory = {
      // rewritten by Object.defineProperty
      location: START,
      // TODO: should be kept in queue
      state: {},
      base,
      createHref: createHref.bind(null, base),
      replace(to) {
        // remove current entry and decrement position
        queue.splice(position--, 1);
        setLocation(to);
      },
      push(to, data) {
        setLocation(to);
      },
      listen(callback) {
        listeners.push(callback);
        return () => {
          const index = listeners.indexOf(callback);
          if (index > -1)
            listeners.splice(index, 1);
        };
      },
      destroy() {
        listeners = [];
        queue = [START];
        position = 0;
      },
      go(delta, shouldTrigger = true) {
        const from = this.location;
        const direction =
            // we are considering delta === 0 going forward, but in abstract mode
            // using 0 for the delta doesn't make sense like it does in html5 where
            // it reloads the page
            delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
        position = Math.max(0, Math.min(position + delta, queue.length - 1));
        if (shouldTrigger) {
          triggerListeners(this.location, from, {
            direction,
            delta,
          });
        }
      },
    };
    Object.defineProperty(routerHistory, 'location', {
      enumerable: true,
      get: () => queue[position],
    });
    return routerHistory;
  }

  /**
   * Creates a hash history. Useful for web applications with no host (e.g.
   * `file://`) or when configuring a server to handle any URL is not possible.
   *
   * @param base - optional base to provide. Defaults to `location.pathname +
   * location.search` If there is a `<base>` tag in the `head`, its value will be
   * ignored in favor of this parameter **but note it affects all the
   * history.pushState() calls**, meaning that if you use a `<base>` tag, it's
   * `href` value **has to match this parameter** (ignoring anything after the
   * `#`).
   *
   * @example
   * ```js
   * // at https://example.com/folder
   * createWebHashHistory() // gives a url of `https://example.com/folder#`
   * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#`
   * // if the `#` is provided in the base, it won't be added by `createWebHashHistory`
   * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/`
   * // you should avoid doing this because it changes the original url and breaks copying urls
   * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#`
   *
   * // at file:///usr/etc/folder/index.html
   * // for locations with no `host`, the base is ignored
   * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#`
   * ```
   */
  function createWebHashHistory(base) {
    // Make sure this implementation is fine in terms of encoding, specially for IE11
    // for `file://`, directly use the pathname and ignore the base
    // location.pathname contains an initial `/` even at the root: `https://example.com`
    base = location.host ? base || location.pathname + location.search : '';
    // allow the user to provide a `#` in the middle: `/base/#/app`
    if (!base.includes('#'))
      base += '#';
    if (!base.endsWith('#/') && !base.endsWith('#')) {
      warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`);
    }
    return createWebHistory(base);
  }

  function isRouteLocation(route) {
    return typeof route === 'string' || (route && typeof route === 'object');
  }
  function isRouteName(name) {
    return typeof name === 'string' || typeof name === 'symbol';
  }

  /**
   * Initial route location where the router is. Can be used in navigation guards
   * to differentiate the initial navigation.
   *
   * @example
   * ```js
   * import { START_LOCATION } from 'vue-router'
   *
   * router.beforeEach((to, from) => {
   *   if (from === START_LOCATION) {
   *     // initial navigation
   *   }
   * })
   * ```
   */
  const START_LOCATION_NORMALIZED = {
    path: '/',
    name: undefined,
    params: {},
    query: {},
    hash: '',
    fullPath: '/',
    matched: [],
    meta: {},
    redirectedFrom: undefined,
  };

  const NavigationFailureSymbol = /*#__PURE__*/ PolySymbol('navigation failure' );
  /**
   * Enumeration with all possible types for navigation failures. Can be passed to
   * {@link isNavigationFailure} to check for specific failures.
   */
  exports.NavigationFailureType = void 0;
  (function (NavigationFailureType) {
    /**
     * An aborted navigation is a navigation that failed because a navigation
     * guard returned `false` or called `next(false)`
     */
    NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted";
    /**
     * A cancelled navigation is a navigation that failed because a more recent
     * navigation finished started (not necessarily finished).
     */
    NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled";
    /**
     * A duplicated navigation is a navigation that failed because it was
     * initiated while already being at the exact same location.
     */
    NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated";
  })(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
  // DEV only debug messages
  const ErrorTypeMessages = {
    [1 /* MATCHER_NOT_FOUND */]({ location, currentLocation }) {
      return `No match for\n ${JSON.stringify(location)}${currentLocation
          ? '\nwhile being at\n' + JSON.stringify(currentLocation)
          : ''}`;
    },
    [2 /* NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
      return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
    },
    [4 /* NAVIGATION_ABORTED */]({ from, to }) {
      return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
    },
    [8 /* NAVIGATION_CANCELLED */]({ from, to }) {
      return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
    },
    [16 /* NAVIGATION_DUPLICATED */]({ from, to }) {
      return `Avoided redundant navigation to current location: "${from.fullPath}".`;
    },
  };
  function createRouterError(type, params) {
    // keep full error messages in cjs versions
    {
      return assign(new Error(ErrorTypeMessages[type](params)), {
        type,
        [NavigationFailureSymbol]: true,
      }, params);
    }
  }
  function isNavigationFailure(error, type) {
    return (error instanceof Error &&
        NavigationFailureSymbol in error &&
        (type == null || !!(error.type & type)));
  }
  const propertiesToLog = ['params', 'query', 'hash'];
  function stringifyRoute(to) {
    if (typeof to === 'string')
      return to;
    if ('path' in to)
      return to.path;
    const location = {};
    for (const key of propertiesToLog) {
      if (key in to)
        location[key] = to[key];
    }
    return JSON.stringify(location, null, 2);
  }

  // default pattern for a param: non greedy everything but /
  const BASE_PARAM_PATTERN = '[^/]+?';
  const BASE_PATH_PARSER_OPTIONS = {
    sensitive: false,
    strict: false,
    start: true,
    end: true,
  };
  // Special Regex characters that must be escaped in static tokens
  const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
  /**
   * Creates a path parser from an array of Segments (a segment is an array of Tokens)
   *
   * @param segments - array of segments returned by tokenizePath
   * @param extraOptions - optional options for the regexp
   * @returns a PathParser
   */
  function tokensToParser(segments, extraOptions) {
    const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
    // the amount of scores is the same as the length of segments except for the root segment "/"
    let score = [];
    // the regexp as a string
    let pattern = options.start ? '^' : '';
    // extracted keys
    const keys = [];
    for (const segment of segments) {
      // the root segment needs special treatment
      const segmentScores = segment.length ? [] : [90 /* Root */];
      // allow trailing slash
      if (options.strict && !segment.length)
        pattern += '/';
      for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
        const token = segment[tokenIndex];
        // resets the score if we are inside a sub segment /:a-other-:b
        let subSegmentScore = 40 /* Segment */ +
            (options.sensitive ? 0.25 /* BonusCaseSensitive */ : 0);
        if (token.type === 0 /* Static */) {
          // prepend the slash if we are starting a new segment
          if (!tokenIndex)
            pattern += '/';
          pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
          subSegmentScore += 40 /* Static */;
        }
        else if (token.type === 1 /* Param */) {
          const { value, repeatable, optional, regexp } = token;
          keys.push({
            name: value,
            repeatable,
            optional,
          });
          const re = regexp ? regexp : BASE_PARAM_PATTERN;
          // the user provided a custom regexp /:id(\\d+)
          if (re !== BASE_PARAM_PATTERN) {
            subSegmentScore += 10 /* BonusCustomRegExp */;
            // make sure the regexp is valid before using it
            try {
              new RegExp(`(${re})`);
            }
            catch (err) {
              throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` +
                  err.message);
            }
          }
          // when we repeat we must take care of the repeating leading slash
          let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`;
          // prepend the slash if we are starting a new segment
          if (!tokenIndex)
            subPattern =
                // avoid an optional / if there are more segments e.g. /:p?-static
                // or /:p?-:p2
                optional && segment.length < 2
                    ? `(?:/${subPattern})`
                    : '/' + subPattern;
          if (optional)
            subPattern += '?';
          pattern += subPattern;
          subSegmentScore += 20 /* Dynamic */;
          if (optional)
            subSegmentScore += -8 /* BonusOptional */;
          if (repeatable)
            subSegmentScore += -20 /* BonusRepeatable */;
          if (re === '.*')
            subSegmentScore += -50 /* BonusWildcard */;
        }
        segmentScores.push(subSegmentScore);
      }
      // an empty array like /home/ -> [[{home}], []]
      // if (!segment.length) pattern += '/'
      score.push(segmentScores);
    }
    // only apply the strict bonus to the last score
    if (options.strict && options.end) {
      const i = score.length - 1;
      score[i][score[i].length - 1] += 0.7000000000000001 /* BonusStrict */;
    }
    // TODO: dev only warn double trailing slash
    if (!options.strict)
      pattern += '/?';
    if (options.end)
      pattern += '$';
    // allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else
    else if (options.strict)
      pattern += '(?:/|$)';
    const re = new RegExp(pattern, options.sensitive ? '' : 'i');
    function parse(path) {
      const match = path.match(re);
      const params = {};
      if (!match)
        return null;
      for (let i = 1; i < match.length; i++) {
        const value = match[i] || '';
        const key = keys[i - 1];
        params[key.name] = value && key.repeatable ? value.split('/') : value;
      }
      return params;
    }
    function stringify(params) {
      let path = '';
      // for optional parameters to allow to be empty
      let avoidDuplicatedSlash = false;
      for (const segment of segments) {
        if (!avoidDuplicatedSlash || !path.endsWith('/'))
          path += '/';
        avoidDuplicatedSlash = false;
        for (const token of segment) {
          if (token.type === 0 /* Static */) {
            path += token.value;
          }
          else if (token.type === 1 /* Param */) {
            const { value, repeatable, optional } = token;
            const param = value in params ? params[value] : '';
            if (Array.isArray(param) && !repeatable)
              throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
            const text = Array.isArray(param) ? param.join('/') : param;
            if (!text) {
              if (optional) {
                // if we have more than one optional param like /:a?-static we
                // don't need to care about the optional param
                if (segment.length < 2) {
                  // remove the last slash as we could be at the end
                  if (path.endsWith('/'))
                    path = path.slice(0, -1);
                  // do not append a slash on the next iteration
                  else
                    avoidDuplicatedSlash = true;
                }
              }
              else
                throw new Error(`Missing required param "${value}"`);
            }
            path += text;
          }
        }
      }
      return path;
    }
    return {
      re,
      score,
      keys,
      parse,
      stringify,
    };
  }
  /**
   * Compares an array of numbers as used in PathParser.score and returns a
   * number. This function can be used to `sort` an array
   * @param a - first array of numbers
   * @param b - second array of numbers
   * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
   * should be sorted first
   */
  function compareScoreArray(a, b) {
    let i = 0;
    while (i < a.length && i < b.length) {
      const diff = b[i] - a[i];
      // only keep going if diff === 0
      if (diff)
        return diff;
      i++;
    }
    // if the last subsegment was Static, the shorter segments should be sorted first
    // otherwise sort the longest segment first
    if (a.length < b.length) {
      return a.length === 1 && a[0] === 40 /* Static */ + 40 /* Segment */
          ? -1
          : 1;
    }
    else if (a.length > b.length) {
      return b.length === 1 && b[0] === 40 /* Static */ + 40 /* Segment */
          ? 1
          : -1;
    }
    return 0;
  }
  /**
   * Compare function that can be used with `sort` to sort an array of PathParser
   * @param a - first PathParser
   * @param b - second PathParser
   * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b
   */
  function comparePathParserScore(a, b) {
    let i = 0;
    const aScore = a.score;
    const bScore = b.score;
    while (i < aScore.length && i < bScore.length) {
      const comp = compareScoreArray(aScore[i], bScore[i]);
      // do not return if both are equal
      if (comp)
        return comp;
      i++;
    }
    // if a and b share the same score entries but b has more, sort b first
    return bScore.length - aScore.length;
    // this is the ternary version
    // return aScore.length < bScore.length
    //   ? 1
    //   : aScore.length > bScore.length
    //   ? -1
    //   : 0
  }

  const ROOT_TOKEN = {
    type: 0 /* Static */,
    value: '',
  };
  const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
  // After some profiling, the cache seems to be unnecessary because tokenizePath
  // (the slowest part of adding a route) is very fast
  // const tokenCache = new Map<string, Token[][]>()
  function tokenizePath(path) {
    if (!path)
      return [[]];
    if (path === '/')
      return [[ROOT_TOKEN]];
    if (!path.startsWith('/')) {
      throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".`
      );
    }
    // if (tokenCache.has(path)) return tokenCache.get(path)!
    function crash(message) {
      throw new Error(`ERR (${state})/"${buffer}": ${message}`);
    }
    let state = 0 /* Static */;
    let previousState = state;
    const tokens = [];
    // the segment will always be valid because we get into the initial state
    // with the leading /
    let segment;
    function finalizeSegment() {
      if (segment)
        tokens.push(segment);
      segment = [];
    }
    // index on the path
    let i = 0;
    // char at index
    let char;
    // buffer of the value read
    let buffer = '';
    // custom regexp for a param
    let customRe = '';
    function consumeBuffer() {
      if (!buffer)
        return;
      if (state === 0 /* Static */) {
        segment.push({
          type: 0 /* Static */,
          value: buffer,
        });
      }
      else if (state === 1 /* Param */ ||
          state === 2 /* ParamRegExp */ ||
          state === 3 /* ParamRegExpEnd */) {
        if (segment.length > 1 && (char === '*' || char === '+'))
          crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
        segment.push({
          type: 1 /* Param */,
          value: buffer,
          regexp: customRe,
          repeatable: char === '*' || char === '+',
          optional: char === '*' || char === '?',
        });
      }
      else {
        crash('Invalid state to consume buffer');
      }
      buffer = '';
    }
    function addCharToBuffer() {
      buffer += char;
    }
    while (i < path.length) {
      char = path[i++];
      if (char === '\\' && state !== 2 /* ParamRegExp */) {
        previousState = state;
        state = 4 /* EscapeNext */;
        continue;
      }
      switch (state) {
        case 0 /* Static */:
          if (char === '/') {
            if (buffer) {
              consumeBuffer();
            }
            finalizeSegment();
          }
          else if (char === ':') {
            consumeBuffer();
            state = 1 /* Param */;
          }
          else {
            addCharToBuffer();
          }
          break;
        case 4 /* EscapeNext */:
          addCharToBuffer();
          state = previousState;
          break;
        case 1 /* Param */:
          if (char === '(') {
            state = 2 /* ParamRegExp */;
          }
          else if (VALID_PARAM_RE.test(char)) {
            addCharToBuffer();
          }
          else {
            consumeBuffer();
            state = 0 /* Static */;
            // go back one character if we were not modifying
            if (char !== '*' && char !== '?' && char !== '+')
              i--;
          }
          break;
        case 2 /* ParamRegExp */:
          // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
          // it already works by escaping the closing )
          // https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB#
          // is this really something people need since you can also write
          // /prefix_:p()_suffix
          if (char === ')') {
            // handle the escaped )
            if (customRe[customRe.length - 1] == '\\')
              customRe = customRe.slice(0, -1) + char;
            else
              state = 3 /* ParamRegExpEnd */;
          }
          else {
            customRe += char;
          }
          break;
        case 3 /* ParamRegExpEnd */:
          // same as finalizing a param
          consumeBuffer();
          state = 0 /* Static */;
          // go back one character if we were not modifying
          if (char !== '*' && char !== '?' && char !== '+')
            i--;
          customRe = '';
          break;
        default:
          crash('Unknown state');
          break;
      }
    }
    if (state === 2 /* ParamRegExp */)
      crash(`Unfinished custom RegExp for param "${buffer}"`);
    consumeBuffer();
    finalizeSegment();
    // tokenCache.set(path, tokens)
    return tokens;
  }

  function createRouteRecordMatcher(record, parent, options) {
    const parser = tokensToParser(tokenizePath(record.path), options);
    // warn against params with the same name
    {
      const existingKeys = new Set();
      for (const key of parser.keys) {
        if (existingKeys.has(key.name))
          warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`);
        existingKeys.add(key.name);
      }
    }
    const matcher = assign(parser, {
      record,
      parent,
      // these needs to be populated by the parent
      children: [],
      alias: [],
    });
    if (parent) {
      // both are aliases or both are not aliases
      // we don't want to mix them because the order is used when
      // passing originalRecord in Matcher.addRoute
      if (!matcher.record.aliasOf === !parent.record.aliasOf)
        parent.children.push(matcher);
    }
    return matcher;
  }

  /**
   * Creates a Router Matcher.
   *
   * @internal
   * @param routes - array of initial routes
   * @param globalOptions - global route options
   */
  function createRouterMatcher(routes, globalOptions) {
    // normalized ordered array of matchers
    const matchers = [];
    const matcherMap = new Map();
    globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions);
    function getRecordMatcher(name) {
      return matcherMap.get(name);
    }
    function addRoute(record, parent, originalRecord) {
      // used later on to remove by name
      let isRootAdd = !originalRecord;
      let mainNormalizedRecord = normalizeRouteRecord(record);
      // we might be the child of an alias
      mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
      const options = mergeOptions(globalOptions, record);
      // generate an array of records to correctly handle aliases
      const normalizedRecords = [
        mainNormalizedRecord,
      ];
      if ('alias' in record) {
        const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias;
        for (const alias of aliases) {
          normalizedRecords.push(assign({}, mainNormalizedRecord, {
            // this allows us to hold a copy of the `components` option
            // so that async components cache is hold on the original record
            components: originalRecord
                ? originalRecord.record.components
                : mainNormalizedRecord.components,
            path: alias,
            // we might be the child of an alias
            aliasOf: originalRecord
                ? originalRecord.record
                : mainNormalizedRecord,
            // the aliases are always of the same kind as the original since they
            // are defined on the same record
          }));
        }
      }
      let matcher;
      let originalMatcher;
      for (const normalizedRecord of normalizedRecords) {
        let { path } = normalizedRecord;
        // Build up the path for nested routes if the child isn't an absolute
        // route. Only add the / delimiter if the child path isn't empty and if the
        // parent path doesn't have a trailing slash
        if (parent && path[0] !== '/') {
          let parentPath = parent.record.path;
          let connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/';
          normalizedRecord.path =
              parent.record.path + (path && connectingSlash + path);
        }
        if (normalizedRecord.path === '*') {
          throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
              'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.');
        }
        // create the object before hand so it can be passed to children
        matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
        if (parent && path[0] === '/')
          checkMissingParamsInAbsolutePath(matcher, parent);
        // if we are an alias we must tell the original record that we exist
        // so we can be removed
        if (originalRecord) {
          originalRecord.alias.push(matcher);
          {
            checkSameParams(originalRecord, matcher);
          }
        }
        else {
          // otherwise, the first record is the original and others are aliases
          originalMatcher = originalMatcher || matcher;
          if (originalMatcher !== matcher)
            originalMatcher.alias.push(matcher);
          // remove the route if named and only for the top record (avoid in nested calls)
          // this works because the original record is the first one
          if (isRootAdd && record.name && !isAliasRecord(matcher))
            removeRoute(record.name);
        }
        if ('children' in mainNormalizedRecord) {
          let children = mainNormalizedRecord.children;
          for (let i = 0; i < children.length; i++) {
            addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
          }
        }
        // if there was no original record, then the first one was not an alias and all
        // other alias (if any) need to reference this record when adding children
        originalRecord = originalRecord || matcher;
        // TODO: add normalized records for more flexibility
        // if (parent && isAliasRecord(originalRecord)) {
        //   parent.children.push(originalRecord)
        // }
        insertMatcher(matcher);
      }
      return originalMatcher
          ? () => {
            // since other matchers are aliases, they should be removed by the original matcher
            removeRoute(originalMatcher);
          }
          : noop;
    }
    function removeRoute(matcherRef) {
      if (isRouteName(matcherRef)) {
        const matcher = matcherMap.get(matcherRef);
        if (matcher) {
          matcherMap.delete(matcherRef);
          matchers.splice(matchers.indexOf(matcher), 1);
          matcher.children.forEach(removeRoute);
          matcher.alias.forEach(removeRoute);
        }
      }
      else {
        let index = matchers.indexOf(matcherRef);
        if (index > -1) {
          matchers.splice(index, 1);
          if (matcherRef.record.name)
            matcherMap.delete(matcherRef.record.name);
          matcherRef.children.forEach(removeRoute);
          matcherRef.alias.forEach(removeRoute);
        }
      }
    }
    function getRoutes() {
      return matchers;
    }
    function insertMatcher(matcher) {
      let i = 0;
      // console.log('i is', { i })
      while (i < matchers.length &&
      comparePathParserScore(matcher, matchers[i]) >= 0)
        i++;
      // console.log('END i is', { i })
      // while (i < matchers.length && matcher.score <= matchers[i].score) i++
      matchers.splice(i, 0, matcher);
      // only add the original record to the name map
      if (matcher.record.name && !isAliasRecord(matcher))
        matcherMap.set(matcher.record.name, matcher);
    }
    function resolve(location, currentLocation) {
      let matcher;
      let params = {};
      let path;
      let name;
      if ('name' in location && location.name) {
        matcher = matcherMap.get(location.name);
        if (!matcher)
          throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
            location,
          });
        name = matcher.record.name;
        params = assign(
            // paramsFromLocation is a new object
            paramsFromLocation(currentLocation.params,
                // only keep params that exist in the resolved location
                // TODO: only keep optional params coming from a parent record
                matcher.keys.filter(k => !k.optional).map(k => k.name)), location.params);
        // throws if cannot be stringified
        path = matcher.stringify(params);
      }
      else if ('path' in location) {
        // no need to resolve the path with the matcher as it was provided
        // this also allows the user to control the encoding
        path = location.path;
        if (!path.startsWith('/')) {
          warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-router-next.`);
        }
        matcher = matchers.find(m => m.re.test(path));
        // matcher should have a value after the loop
        if (matcher) {
          // TODO: dev warning of unused params if provided
          // we know the matcher works because we tested the regexp
          params = matcher.parse(path);
          name = matcher.record.name;
        }
        // location is a relative path
      }
      else {
        // match by name or path of current route
        matcher = currentLocation.name
            ? matcherMap.get(currentLocation.name)
            : matchers.find(m => m.re.test(currentLocation.path));
        if (!matcher)
          throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
            location,
            currentLocation,
          });
        name = matcher.record.name;
        // since we are navigating to the same location, we don't need to pick the
        // params like when `name` is provided
        params = assign({}, currentLocation.params, location.params);
        path = matcher.stringify(params);
      }
      const matched = [];
      let parentMatcher = matcher;
      while (parentMatcher) {
        // reversed order so parents are at the beginning
        matched.unshift(parentMatcher.record);
        parentMatcher = parentMatcher.parent;
      }
      return {
        name,
        path,
        params,
        matched,
        meta: mergeMetaFields(matched),
      };
    }
    // add initial routes
    routes.forEach(route => addRoute(route));
    return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher };
  }
  function paramsFromLocation(params, keys) {
    let newParams = {};
    for (let key of keys) {
      if (key in params)
        newParams[key] = params[key];
    }
    return newParams;
  }
  /**
   * Normalizes a RouteRecordRaw. Creates a copy
   *
   * @param record
   * @returns the normalized version
   */
  function normalizeRouteRecord(record) {
    return {
      path: record.path,
      redirect: record.redirect,
      name: record.name,
      meta: record.meta || {},
      aliasOf: undefined,
      beforeEnter: record.beforeEnter,
      props: normalizeRecordProps(record),
      children: record.children || [],
      instances: {},
      leaveGuards: new Set(),
      updateGuards: new Set(),
      enterCallbacks: {},
      components: 'components' in record
          ? record.components || {}
          : { default: record.component },
    };
  }
  /**
   * Normalize the optional `props` in a record to always be an object similar to
   * components. Also accept a boolean for components.
   * @param record
   */
  function normalizeRecordProps(record) {
    const propsObject = {};
    // props does not exist on redirect records but we can set false directly
    const props = record.props || false;
    if ('component' in record) {
      propsObject.default = props;
    }
    else {
      // NOTE: we could also allow a function to be applied to every component.
      // Would need user feedback for use cases
      for (let name in record.components)
        propsObject[name] = typeof props === 'boolean' ? props : props[name];
    }
    return propsObject;
  }
  /**
   * Checks if a record or any of its parent is an alias
   * @param record
   */
  function isAliasRecord(record) {
    while (record) {
      if (record.record.aliasOf)
        return true;
      record = record.parent;
    }
    return false;
  }
  /**
   * Merge meta fields of an array of records
   *
   * @param matched - array of matched records
   */
  function mergeMetaFields(matched) {
    return matched.reduce((meta, record) => assign(meta, record.meta), {});
  }
  function mergeOptions(defaults, partialOptions) {
    let options = {};
    for (let key in defaults) {
      options[key] =
          key in partialOptions ? partialOptions[key] : defaults[key];
    }
    return options;
  }
  function isSameParam(a, b) {
    return (a.name === b.name &&
        a.optional === b.optional &&
        a.repeatable === b.repeatable);
  }
  /**
   * Check if a path and its alias have the same required params
   *
   * @param a - original record
   * @param b - alias record
   */
  function checkSameParams(a, b) {
    for (let key of a.keys) {
      if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
        return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
    }
    for (let key of b.keys) {
      if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
        return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
    }
  }
  function checkMissingParamsInAbsolutePath(record, parent) {
    for (let key of parent.keys) {
      if (!record.keys.find(isSameParam.bind(null, key)))
        return warn(`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
    }
  }

  /**
   * Encoding Rules ␣ = Space Path: ␣ " < > # ? { } Query: ␣ " < > # & = Hash: ␣ "
   * < > `
   *
   * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
   * defines some extra characters to be encoded. Most browsers do not encode them
   * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
   * also encode `!'()*`. Leaving unencoded only ASCII alphanumeric(`a-zA-Z0-9`)
   * plus `-._~`. This extra safety should be applied to query by patching the
   * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
   * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
   * into a `/` if directly typed in. The _backtick_ (`````) should also be
   * encoded everywhere because some browsers like FF encode it when directly
   * written while others don't. Safari and IE don't encode ``"<>{}``` in hash.
   */
      // const EXTRA_RESERVED_RE = /[!'()*]/g
      // const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16)
  const HASH_RE = /#/g; // %23
  const AMPERSAND_RE = /&/g; // %26
  const SLASH_RE = /\//g; // %2F
  const EQUAL_RE = /=/g; // %3D
  const IM_RE = /\?/g; // %3F
  const PLUS_RE = /\+/g; // %2B
  /**
   * NOTE: It's not clear to me if we should encode the + symbol in queries, it
   * seems to be less flexible than not doing so and I can't find out the legacy
   * systems requiring this for regular requests like text/html. In the standard,
   * the encoding of the plus character is only mentioned for
   * application/x-www-form-urlencoded
   * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
   * leave the plus character as is in queries. To be more flexible, we allow the
   * plus character on the query but it can also be manually encoded by the user.
   *
   * Resources:
   * - https://url.spec.whatwg.org/#urlencoded-parsing
   * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20
   */
  const ENC_BRACKET_OPEN_RE = /%5B/g; // [
  const ENC_BRACKET_CLOSE_RE = /%5D/g; // ]
  const ENC_CARET_RE = /%5E/g; // ^
  const ENC_BACKTICK_RE = /%60/g; // `
  const ENC_CURLY_OPEN_RE = /%7B/g; // {
  const ENC_PIPE_RE = /%7C/g; // |
  const ENC_CURLY_CLOSE_RE = /%7D/g; // }
  const ENC_SPACE_RE = /%20/g; // }
  /**
   * Encode characters that need to be encoded on the path, search and hash
   * sections of the URL.
   *
   * @internal
   * @param text - string to encode
   * @returns encoded string
   */
  function commonEncode(text) {
    return encodeURI('' + text)
        .replace(ENC_PIPE_RE, '|')
        .replace(ENC_BRACKET_OPEN_RE, '[')
        .replace(ENC_BRACKET_CLOSE_RE, ']');
  }
  /**
   * Encode characters that need to be encoded on the hash section of the URL.
   *
   * @param text - string to encode
   * @returns encoded string
   */
  function encodeHash(text) {
    return commonEncode(text)
        .replace(ENC_CURLY_OPEN_RE, '{')
        .replace(ENC_CURLY_CLOSE_RE, '}')
        .replace(ENC_CARET_RE, '^');
  }
  /**
   * Encode characters that need to be encoded query values on the query
   * section of the URL.
   *
   * @param text - string to encode
   * @returns encoded string
   */
  function encodeQueryValue(text) {
    return (commonEncode(text)
        // Encode the space as +, encode the + to differentiate it from the space
        .replace(PLUS_RE, '%2B')
        .replace(ENC_SPACE_RE, '+')
        .replace(HASH_RE, '%23')
        .replace(AMPERSAND_RE, '%26')
        .replace(ENC_BACKTICK_RE, '`')
        .replace(ENC_CURLY_OPEN_RE, '{')
        .replace(ENC_CURLY_CLOSE_RE, '}')
        .replace(ENC_CARET_RE, '^'));
  }
  /**
   * Like `encodeQueryValue` but also encodes the `=` character.
   *
   * @param text - string to encode
   */
  function encodeQueryKey(text) {
    return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
  }
  /**
   * Encode characters that need to be encoded on the path section of the URL.
   *
   * @param text - string to encode
   * @returns encoded string
   */
  function encodePath(text) {
    return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
  }
  /**
   * Encode characters that need to be encoded on the path section of the URL as a
   * param. This function encodes everything {@link encodePath} does plus the
   * slash (`/`) character.
   *
   * @param text - string to encode
   * @returns encoded string
   */
  function encodeParam(text) {
    return encodePath(text).replace(SLASH_RE, '%2F');
  }
  /**
   * Decode text using `decodeURIComponent`. Returns the original text if it
   * fails.
   *
   * @param text - string to decode
   * @returns decoded string
   */
  function decode(text) {
    try {
      return decodeURIComponent('' + text);
    }
    catch (err) {
      warn(`Error decoding "${text}". Using original value`);
    }
    return '' + text;
  }

  /**
   * Transforms a queryString into a {@link LocationQuery} object. Accept both, a
   * version with the leading `?` and without Should work as URLSearchParams

   * @internal
   *
   * @param search - search string to parse
   * @returns a query object
   */
  function parseQuery(search) {
    const query = {};
    // avoid creating an object with an empty key and empty value
    // because of split('&')
    if (search === '' || search === '?')
      return query;
    const hasLeadingIM = search[0] === '?';
    const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
    for (let i = 0; i < searchParams.length; ++i) {
      // pre decode the + into space
      const searchParam = searchParams[i].replace(PLUS_RE, ' ');
      // allow the = character
      let eqPos = searchParam.indexOf('=');
      let key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
      let value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
      if (key in query) {
        // an extra variable for ts types
        let currentValue = query[key];
        if (!Array.isArray(currentValue)) {
          currentValue = query[key] = [currentValue];
        }
        currentValue.push(value);
      }
      else {
        query[key] = value;
      }
    }
    return query;
  }
  /**
   * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it
   * doesn't prepend a `?`
   *
   * @internal
   *
   * @param query - query object to stringify
   * @returns string version of the query without the leading `?`
   */
  function stringifyQuery(query) {
    let search = '';
    for (let key in query) {
      const value = query[key];
      key = encodeQueryKey(key);
      if (value == null) {
        // only null adds the value
        if (value !== undefined) {
          search += (search.length ? '&' : '') + key;
        }
        continue;
      }
      // keep null values
      let values = Array.isArray(value)
          ? value.map(v => v && encodeQueryValue(v))
          : [value && encodeQueryValue(value)];
      values.forEach(value => {
        // skip undefined values in arrays as if they were not present
        // smaller code than using filter
        if (value !== undefined) {
          // only append & with non-empty search
          search += (search.length ? '&' : '') + key;
          if (value != null)
            search += '=' + value;
        }
      });
    }
    return search;
  }
  /**
   * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting
   * numbers into strings, removing keys with an undefined value and replacing
   * undefined with null in arrays
   *
   * @param query - query object to normalize
   * @returns a normalized query object
   */
  function normalizeQuery(query) {
    const normalizedQuery = {};
    for (let key in query) {
      let value = query[key];
      if (value !== undefined) {
        normalizedQuery[key] = Array.isArray(value)
            ? value.map(v => (v == null ? null : '' + v))
            : value == null
                ? value
                : '' + value;
      }
    }
    return normalizedQuery;
  }

  /**
   * Create a list of callbacks that can be reset. Used to create before and after navigation guards list
   */
  function useCallbacks() {
    let handlers = [];
    function add(handler) {
      handlers.push(handler);
      return () => {
        const i = handlers.indexOf(handler);
        if (i > -1)
          handlers.splice(i, 1);
      };
    }
    function reset() {
      handlers = [];
    }
    return {
      add,
      list: () => handlers,
      reset,
    };
  }

  function registerGuard(record, name, guard) {
    const removeFromList = () => {
      record[name].delete(guard);
    };
    vue.onUnmounted(removeFromList);
    vue.onDeactivated(removeFromList);
    vue.onActivated(() => {
      record[name].add(guard);
    });
    record[name].add(guard);
  }
  /**
   * Add a navigation guard that triggers whenever the component for the current
   * location is about to be left. Similar to {@link beforeRouteLeave} but can be
   * used in any component. The guard is removed when the component is unmounted.
   *
   * @param leaveGuard - {@link NavigationGuard}
   */
  function onBeforeRouteLeave(leaveGuard) {
    if (!vue.getCurrentInstance()) {
      warn('getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function');
      return;
    }
    const activeRecord = vue.inject(matchedRouteKey, {}).value;
    if (!activeRecord) {
      warn('No active route record was found. Are you missing a <router-view> component?');
      return;
    }
    registerGuard(activeRecord, 'leaveGuards', leaveGuard);
  }
  /**
   * Add a navigation guard that triggers whenever the current location is about
   * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any
   * component. The guard is removed when the component is unmounted.
   *
   * @param updateGuard - {@link NavigationGuard}
   */
  function onBeforeRouteUpdate(updateGuard) {
    if (!vue.getCurrentInstance()) {
      warn('getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function');
      return;
    }
    const activeRecord = vue.inject(matchedRouteKey, {}).value;
    if (!activeRecord) {
      warn('No active route record was found. Are you missing a <router-view> component?');
      return;
    }
    registerGuard(activeRecord, 'updateGuards', updateGuard);
  }
  function guardToPromiseFn(guard, to, from, record, name) {
    // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place
    const enterCallbackArray = record &&
        // name is defined if record is because of the function overload
        (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
    return () => new Promise((resolve, reject) => {
      const next = (valid) => {
        if (valid === false)
          reject(createRouterError(4 /* NAVIGATION_ABORTED */, {
            from,
            to,
          }));
        else if (valid instanceof Error) {
          reject(valid);
        }
        else if (isRouteLocation(valid)) {
          reject(createRouterError(2 /* NAVIGATION_GUARD_REDIRECT */, {
            from: to,
            to: valid,
          }));
        }
        else {
          if (enterCallbackArray &&
              // since enterCallbackArray is truthy, both record and name also are
              record.enterCallbacks[name] === enterCallbackArray &&
              typeof valid === 'function')
            enterCallbackArray.push(valid);
          resolve();
        }
      };
      // wrapping with Promise.resolve allows it to work with both async and sync guards
      const guardReturn = guard.call(record && record.instances[name], to, from, canOnlyBeCalledOnce(next, to, from) );
      let guardCall = Promise.resolve(guardReturn);
      if (guard.length < 3)
        guardCall = guardCall.then(next);
      if (guard.length > 2) {
        const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
        if (typeof guardReturn === 'object' && 'then' in guardReturn) {
          guardCall = guardCall.then(resolvedValue => {
            // @ts-expect-error: _called is added at canOnlyBeCalledOnce
            if (!next._called) {
              warn(message);
              return Promise.reject(new Error('Invalid navigation guard'));
            }
            return resolvedValue;
          });
          // TODO: test me!
        }
        else if (guardReturn !== undefined) {
          // @ts-expect-error: _called is added at canOnlyBeCalledOnce
          if (!next._called) {
            warn(message);
            reject(new Error('Invalid navigation guard'));
            return;
          }
        }
      }
      guardCall.catch(err => reject(err));
    });
  }
  function canOnlyBeCalledOnce(next, to, from) {
    let called = 0;
    return function () {
      if (called++ === 1)
        warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`);
      // @ts-expect-error: we put it in the original one because it's easier to check
      next._called = true;
      if (called === 1)
        next.apply(null, arguments);
    };
  }
  function extractComponentsGuards(matched, guardType, to, from) {
    const guards = [];
    for (const record of matched) {
      for (const name in record.components) {
        let rawComponent = record.components[name];
        {
          if (!rawComponent ||
              (typeof rawComponent !== 'object' &&
                  typeof rawComponent !== 'function')) {
            warn(`Component "${name}" in record with path "${record.path}" is not` +
                ` a valid component. Received "${String(rawComponent)}".`);
            // throw to ensure we stop here but warn to ensure the message isn't
            // missed by the user
            throw new Error('Invalid route component');
          }
          else if ('then' in rawComponent) {
            // warn if user wrote import('/component.vue') instead of () =>
            // import('./component.vue')
            warn(`Component "${name}" in record with path "${record.path}" is a ` +
                `Promise instead of a function that returns a Promise. Did you ` +
                `write "import('./MyPage.vue')" instead of ` +
                `"() => import('./MyPage.vue')" ? This will break in ` +
                `production if not fixed.`);
            let promise = rawComponent;
            rawComponent = () => promise;
          }
          else if (rawComponent.__asyncLoader &&
              // warn only once per component
              !rawComponent.__warnedDefineAsync) {
            rawComponent.__warnedDefineAsync = true;
            warn(`Component "${name}" in record with path "${record.path}" is defined ` +
                `using "defineAsyncComponent()". ` +
                `Write "() => import('./MyPage.vue')" instead of ` +
                `"defineAsyncComponent(() => import('./MyPage.vue'))".`);
          }
        }
        // skip update and leave guards if the route component is not mounted
        if (guardType !== 'beforeRouteEnter' && !record.instances[name])
          continue;
        if (isRouteComponent(rawComponent)) {
          // __vccOpts is added by vue-class-component and contain the regular options
          let options = rawComponent.__vccOpts || rawComponent;
          const guard = options[guardType];
          guard && guards.push(guardToPromiseFn(guard, to, from, record, name));
        }
        else {
          // start requesting the chunk already
          let componentPromise = rawComponent();
          if (!('catch' in componentPromise)) {
            warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
            componentPromise = Promise.resolve(componentPromise);
          }
          guards.push(() => componentPromise.then(resolved => {
            if (!resolved)
              return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`));
            const resolvedComponent = isESModule(resolved)
                ? resolved.default
                : resolved;
            // replace the function with the resolved component
            record.components[name] = resolvedComponent;
            // __vccOpts is added by vue-class-component and contain the regular options
            let options = resolvedComponent.__vccOpts || resolvedComponent;
            const guard = options[guardType];
            return guard && guardToPromiseFn(guard, to, from, record, name)();
          }));
        }
      }
    }
    return guards;
  }
  /**
   * Allows differentiating lazy components from functional components and vue-class-component
   * @param component
   */
  function isRouteComponent(component) {
    return (typeof component === 'object' ||
        'displayName' in component ||
        'props' in component ||
        '__vccOpts' in component);
  }

  // TODO: we could allow currentRoute as a prop to expose `isActive` and
  // `isExactActive` behavior should go through an RFC
  function useLink(props) {
    const router = vue.inject(routerKey);
    const currentRoute = vue.inject(routeLocationKey);
    const route = vue.computed(() => router.resolve(vue.unref(props.to)));
    const activeRecordIndex = vue.computed(() => {
      let { matched } = route.value;
      let { length } = matched;
      const routeMatched = matched[length - 1];
      let currentMatched = currentRoute.matched;
      if (!routeMatched || !currentMatched.length)
        return -1;
      let index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
      if (index > -1)
        return index;
      // possible parent record
      let parentRecordPath = getOriginalPath(matched[length - 2]);
      return (
          // we are dealing with nested routes
          length > 1 &&
          // if the parent and matched route have the same path, this link is
          // referring to the empty child. Or we currently are on a different
          // child of the same parent
          getOriginalPath(routeMatched) === parentRecordPath &&
          // avoid comparing the child with its parent
          currentMatched[currentMatched.length - 1].path !== parentRecordPath
              ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2]))
              : index);
    });
    const isActive = vue.computed(() => activeRecordIndex.value > -1 &&
        includesParams(currentRoute.params, route.value.params));
    const isExactActive = vue.computed(() => activeRecordIndex.value > -1 &&
        activeRecordIndex.value === currentRoute.matched.length - 1 &&
        isSameRouteLocationParams(currentRoute.params, route.value.params));
    function navigate(e = {}) {
      if (guardEvent(e)) {
        return router[vue.unref(props.replace) ? 'replace' : 'push'](vue.unref(props.to)
            // avoid uncaught errors are they are logged anyway
        ).catch(noop);
      }
      return Promise.resolve();
    }
    // devtools only
    if (isBrowser) {
      const instance = vue.getCurrentInstance();
      if (instance) {
        const linkContextDevtools = {
          route: route.value,
          isActive: isActive.value,
          isExactActive: isExactActive.value,
        };
        // @ts-expect-error: this is internal
        instance.__vrl_devtools = instance.__vrl_devtools || [];
        // @ts-expect-error: this is internal
        instance.__vrl_devtools.push(linkContextDevtools);
        vue.watchEffect(() => {
          linkContextDevtools.route = route.value;
          linkContextDevtools.isActive = isActive.value;
          linkContextDevtools.isExactActive = isExactActive.value;
        }, { flush: 'post' });
      }
    }
    return {
      route,
      href: vue.computed(() => route.value.href),
      isActive,
      isExactActive,
      navigate,
    };
  }
  const RouterLinkImpl = /*#__PURE__*/ vue.defineComponent({
    name: 'RouterLink',
    props: {
      to: {
        type: [String, Object],
        required: true,
      },
      replace: Boolean,
      activeClass: String,
      // inactiveClass: String,
      exactActiveClass: String,
      custom: Boolean,
      ariaCurrentValue: {
        type: String,
        default: 'page',
      },
    },
    useLink,
    setup(props, { slots }) {
      const link = vue.reactive(useLink(props));
      const { options } = vue.inject(routerKey);
      const elClass = vue.computed(() => ({
        [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive,
        // [getLinkClass(
        //   props.inactiveClass,
        //   options.linkInactiveClass,
        //   'router-link-inactive'
        // )]: !link.isExactActive,
        [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive,
      }));
      return () => {
        const children = slots.default && slots.default(link);
        return props.custom
            ? children
            : vue.h('a', {
              'aria-current': link.isExactActive
                  ? props.ariaCurrentValue
                  : null,
              href: link.href,
              // this would override user added attrs but Vue will still add
              // the listener so we end up triggering both
              onClick: link.navigate,
              class: elClass.value,
            }, children);
      };
    },
  });
  // export the public type for h/tsx inference
  // also to avoid inline import() in generated d.ts files
  /**
   * Component to render a link that triggers a navigation on click.
   */
  const RouterLink = RouterLinkImpl;
  function guardEvent(e) {
    // don't redirect with control keys
    if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
      return;
    // don't redirect when preventDefault called
    if (e.defaultPrevented)
      return;
    // don't redirect on right click
    if (e.button !== undefined && e.button !== 0)
      return;
    // don't redirect if `target="_blank"`
    // @ts-expect-error getAttribute does exist
    if (e.currentTarget && e.currentTarget.getAttribute) {
      // @ts-expect-error getAttribute exists
      const target = e.currentTarget.getAttribute('target');
      if (/\b_blank\b/i.test(target))
        return;
    }
    // this may be a Weex event which doesn't have this method
    if (e.preventDefault)
      e.preventDefault();
    return true;
  }
  function includesParams(outer, inner) {
    for (let key in inner) {
      let innerValue = inner[key];
      let outerValue = outer[key];
      if (typeof innerValue === 'string') {
        if (innerValue !== outerValue)
          return false;
      }
      else {
        if (!Array.isArray(outerValue) ||
            outerValue.length !== innerValue.length ||
            innerValue.some((value, i) => value !== outerValue[i]))
          return false;
      }
    }
    return true;
  }
  /**
   * Get the original path value of a record by following its aliasOf
   * @param record
   */
  function getOriginalPath(record) {
    return record ? (record.aliasOf ? record.aliasOf.path : record.path) : '';
  }
  /**
   * Utility class to get the active class based on defaults.
   * @param propClass
   * @param globalClass
   * @param defaultClass
   */
  const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null
      ? propClass
      : globalClass != null
          ? globalClass
          : defaultClass;

  const RouterViewImpl = /*#__PURE__*/ vue.defineComponent({
    name: 'RouterView',
    // #674 we manually inherit them
    inheritAttrs: false,
    props: {
      name: {
        type: String,
        default: 'default',
      },
      route: Object,
    },
    setup(props, { attrs, slots }) {
      warnDeprecatedUsage();
      const injectedRoute = vue.inject(routerViewLocationKey);
      const routeToDisplay = vue.computed(() => props.route || injectedRoute.value);
      const depth = vue.inject(viewDepthKey, 0);
      const matchedRouteRef = vue.computed(() => routeToDisplay.value.matched[depth]);
      vue.provide(viewDepthKey, depth + 1);
      vue.provide(matchedRouteKey, matchedRouteRef);
      vue.provide(routerViewLocationKey, routeToDisplay);
      const viewRef = vue.ref();
      // watch at the same time the component instance, the route record we are
      // rendering, and the name
      vue.watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
        // copy reused instances
        if (to) {
          // this will update the instance for new instances as well as reused
          // instances when navigating to a new route
          to.instances[name] = instance;
          // the component instance is reused for a different route or name so
          // we copy any saved update or leave guards. With async setup, the
          // mounting component will mount before the matchedRoute changes,
          // making instance === oldInstance, so we check if guards have been
          // added before. This works because we remove guards when
          // unmounting/deactivating components
          if (from && from !== to && instance && instance === oldInstance) {
            if (!to.leaveGuards.size) {
              to.leaveGuards = from.leaveGuards;
            }
            if (!to.updateGuards.size) {
              to.updateGuards = from.updateGuards;
            }
          }
        }
        // trigger beforeRouteEnter next callbacks
        if (instance &&
            to &&
            // if there is no instance but to and from are the same this might be
            // the first visit
            (!from || !isSameRouteRecord(to, from) || !oldInstance)) {
          (to.enterCallbacks[name] || []).forEach(callback => callback(instance));
        }
      }, { flush: 'post' });
      return () => {
        const route = routeToDisplay.value;
        const matchedRoute = matchedRouteRef.value;
        const ViewComponent = matchedRoute && matchedRoute.components[props.name];
        // we need the value at the time we render because when we unmount, we
        // navigated to a different location so the value is different
        const currentName = props.name;
        if (!ViewComponent) {
          return normalizeSlot(slots.default, { Component: ViewComponent, route });
        }
        // props from route configuration
        const routePropsOption = matchedRoute.props[props.name];
        const routeProps = routePropsOption
            ? routePropsOption === true
                ? route.params
                : typeof routePropsOption === 'function'
                    ? routePropsOption(route)
                    : routePropsOption
            : null;
        const onVnodeUnmounted = vnode => {
          // remove the instance reference to prevent leak
          if (vnode.component.isUnmounted) {
            matchedRoute.instances[currentName] = null;
          }
        };
        const component = vue.h(ViewComponent, assign({}, routeProps, attrs, {
          onVnodeUnmounted,
          ref: viewRef,
        }));
        return (
            // pass the vnode to the slot as a prop.
            // h and <component :is="..."> both accept vnodes
            normalizeSlot(slots.default, { Component: component, route }) ||
            component);
      };
    },
  });
  function normalizeSlot(slot, data) {
    if (!slot)
      return null;
    const slotContent = slot(data);
    return slotContent.length === 1 ? slotContent[0] : slotContent;
  }
  // export the public type for h/tsx inference
  // also to avoid inline import() in generated d.ts files
  /**
   * Component to display the current route the user is at.
   */
  const RouterView = RouterViewImpl;
  // warn against deprecated usage with <transition> & <keep-alive>
  // due to functional component being no longer eager in Vue 3
  function warnDeprecatedUsage() {
    const instance = vue.getCurrentInstance();
    const parentName = instance.parent && instance.parent.type.name;
    if (parentName &&
        (parentName === 'KeepAlive' || parentName.includes('Transition'))) {
      const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition';
      warn(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.\n` +
          `Use slot props instead:\n\n` +
          `<router-view v-slot="{ Component }">\n` +
          `  <${comp}>\n` +
          `    <component :is="Component" />\n` +
          `  </${comp}>\n` +
          `</router-view>`);
    }
  }

  function getDevtoolsGlobalHook() {
    return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
  }
  function getTarget() {
    // @ts-ignore
    return typeof navigator !== 'undefined'
        ? window
        : typeof global !== 'undefined'
            ? global
            : {};
  }

  const HOOK_SETUP = 'devtools-plugin:setup';

  function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
    const hook = getDevtoolsGlobalHook();
    if (hook) {
      hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
    }
    else {
      const target = getTarget();
      const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
      list.push({
        pluginDescriptor,
        setupFn
      });
    }
  }

  function formatRouteLocation(routeLocation, tooltip) {
    const copy = assign({}, routeLocation, {
      // remove variables that can contain vue instances
      matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children', 'aliasOf'])),
    });
    return {
      _custom: {
        type: null,
        readOnly: true,
        display: routeLocation.fullPath,
        tooltip,
        value: copy,
      },
    };
  }
  function formatDisplay(display) {
    return {
      _custom: {
        display,
      },
    };
  }
  // to support multiple router instances
  let routerId = 0;
  function addDevtools(app, router, matcher) {
    // Take over router.beforeEach and afterEach
    // make sure we are not registering the devtool twice
    if (router.__hasDevtools)
      return;
    router.__hasDevtools = true;
    // increment to support multiple router instances
    const id = routerId++;
    setupDevtoolsPlugin({
      id: 'org.vuejs.router' + (id ? '.' + id : ''),
      label: 'Vue Router',
      packageName: 'vue-router',
      homepage: 'https://next.router.vuejs.org/',
      logo: 'https://vuejs.org/images/icons/favicon-96x96.png',
      componentStateTypes: ['Routing'],
      app,
    }, api => {
      // display state added by the router
      api.on.inspectComponent((payload, ctx) => {
        if (payload.instanceData) {
          payload.instanceData.state.push({
            type: 'Routing',
            key: '$route',
            editable: false,
            value: formatRouteLocation(router.currentRoute.value, 'Current Route'),
          });
        }
      });
      // mark router-link as active
      api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
        // if multiple useLink are used
        if (Array.isArray(componentInstance.__vrl_devtools)) {
          componentInstance.__devtoolsApi = api;
          componentInstance.__vrl_devtools.forEach(devtoolsData => {
            let backgroundColor = ORANGE_400;
            let tooltip = '';
            if (devtoolsData.isExactActive) {
              backgroundColor = LIME_500;
              tooltip = 'This is exactly active';
            }
            else if (devtoolsData.isActive) {
              backgroundColor = BLUE_600;
              tooltip = 'This link is active';
            }
            node.tags.push({
              label: devtoolsData.route.path,
              textColor: 0,
              tooltip,
              backgroundColor,
            });
          });
        }
      });
      vue.watch(router.currentRoute, () => {
        // refresh active state
        refreshRoutesView();
        api.notifyComponentUpdate();
        api.sendInspectorTree(routerInspectorId);
        api.sendInspectorState(routerInspectorId);
      });
      const navigationsLayerId = 'router:navigations:' + id;
      api.addTimelineLayer({
        id: navigationsLayerId,
        label: `Router${id ? ' ' + id : ''} Navigations`,
        color: 0x40a8c4,
      });
      // const errorsLayerId = 'router:errors'
      // api.addTimelineLayer({
      //   id: errorsLayerId,
      //   label: 'Router Errors',
      //   color: 0xea5455,
      // })
      router.onError((error, to) => {
        api.addTimelineEvent({
          layerId: navigationsLayerId,
          event: {
            title: 'Error during Navigation',
            subtitle: to.fullPath,
            logType: 'error',
            time: Date.now(),
            data: { error },
            groupId: to.meta.__navigationId,
          },
        });
      });
      // attached to `meta` and used to group events
      let navigationId = 0;
      router.beforeEach((to, from) => {
        const data = {
          guard: formatDisplay('beforeEach'),
          from: formatRouteLocation(from, 'Current Location during this navigation'),
          to: formatRouteLocation(to, 'Target location'),
        };
        // Used to group navigations together, hide from devtools
        Object.defineProperty(to.meta, '__navigationId', {
          value: navigationId++,
        });
        api.addTimelineEvent({
          layerId: navigationsLayerId,
          event: {
            time: Date.now(),
            title: 'Start of navigation',
            subtitle: to.fullPath,
            data,
            groupId: to.meta.__navigationId,
          },
        });
      });
      router.afterEach((to, from, failure) => {
        const data = {
          guard: formatDisplay('afterEach'),
        };
        if (failure) {
          data.failure = {
            _custom: {
              type: Error,
              readOnly: true,
              display: failure ? failure.message : '',
              tooltip: 'Navigation Failure',
              value: failure,
            },
          };
          data.status = formatDisplay('❌');
        }
        else {
          data.status = formatDisplay('✅');
        }
        // we set here to have the right order
        data.from = formatRouteLocation(from, 'Current Location during this navigation');
        data.to = formatRouteLocation(to, 'Target location');
        api.addTimelineEvent({
          layerId: navigationsLayerId,
          event: {
            title: 'End of navigation',
            subtitle: to.fullPath,
            time: Date.now(),
            data,
            logType: failure ? 'warning' : 'default',
            groupId: to.meta.__navigationId,
          },
        });
      });
      /**
       * Inspector of Existing routes
       */
      const routerInspectorId = 'router-inspector:' + id;
      api.addInspector({
        id: routerInspectorId,
        label: 'Routes' + (id ? ' ' + id : ''),
        icon: 'book',
        treeFilterPlaceholder: 'Search routes',
      });
      function refreshRoutesView() {
        // the routes view isn't active
        if (!activeRoutesPayload)
          return;
        const payload = activeRoutesPayload;
        // children routes will appear as nested
        let routes = matcher.getRoutes().filter(route => !route.parent);
        // reset match state to false
        routes.forEach(resetMatchStateOnRouteRecord);
        // apply a match state if there is a payload
        if (payload.filter) {
          routes = routes.filter(route =>
              // save matches state based on the payload
              isRouteMatching(route, payload.filter.toLowerCase()));
        }
        // mark active routes
        routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value));
        payload.rootNodes = routes.map(formatRouteRecordForInspector);
      }
      let activeRoutesPayload;
      api.on.getInspectorTree(payload => {
        activeRoutesPayload = payload;
        if (payload.app === app && payload.inspectorId === routerInspectorId) {
          refreshRoutesView();
        }
      });
      /**
       * Display information about the currently selected route record
       */
      api.on.getInspectorState(payload => {
        if (payload.app === app && payload.inspectorId === routerInspectorId) {
          const routes = matcher.getRoutes();
          const route = routes.find(route => route.record.__vd_id === payload.nodeId);
          if (route) {
            payload.state = {
              options: formatRouteRecordMatcherForStateInspector(route),
            };
          }
        }
      });
      api.sendInspectorTree(routerInspectorId);
      api.sendInspectorState(routerInspectorId);
    });
  }
  function modifierForKey(key) {
    if (key.optional) {
      return key.repeatable ? '*' : '?';
    }
    else {
      return key.repeatable ? '+' : '';
    }
  }
  function formatRouteRecordMatcherForStateInspector(route) {
    const { record } = route;
    const fields = [
      { editable: false, key: 'path', value: record.path },
    ];
    if (record.name != null) {
      fields.push({
        editable: false,
        key: 'name',
        value: record.name,
      });
    }
    fields.push({ editable: false, key: 'regexp', value: route.re });
    if (route.keys.length) {
      fields.push({
        editable: false,
        key: 'keys',
        value: {
          _custom: {
            type: null,
            readOnly: true,
            display: route.keys
                .map(key => `${key.name}${modifierForKey(key)}`)
                .join(' '),
            tooltip: 'Param keys',
            value: route.keys,
          },
        },
      });
    }
    if (record.redirect != null) {
      fields.push({
        editable: false,
        key: 'redirect',
        value: record.redirect,
      });
    }
    if (route.alias.length) {
      fields.push({
        editable: false,
        key: 'aliases',
        value: route.alias.map(alias => alias.record.path),
      });
    }
    fields.push({
      key: 'score',
      editable: false,
      value: {
        _custom: {
          type: null,
          readOnly: true,
          display: route.score.map(score => score.join(', ')).join(' | '),
          tooltip: 'Score used to sort routes',
          value: route.score,
        },
      },
    });
    return fields;
  }
  /**
   * Extracted from tailwind palette
   */
  const PINK_500 = 0xec4899;
  const BLUE_600 = 0x2563eb;
  const LIME_500 = 0x84cc16;
  const CYAN_400 = 0x22d3ee;
  const ORANGE_400 = 0xfb923c;
  // const GRAY_100 = 0xf4f4f5
  const DARK = 0x666666;
  function formatRouteRecordForInspector(route) {
    const tags = [];
    const { record } = route;
    if (record.name != null) {
      tags.push({
        label: String(record.name),
        textColor: 0,
        backgroundColor: CYAN_400,
      });
    }
    if (record.aliasOf) {
      tags.push({
        label: 'alias',
        textColor: 0,
        backgroundColor: ORANGE_400,
      });
    }
    if (route.__vd_match) {
      tags.push({
        label: 'matches',
        textColor: 0,
        backgroundColor: PINK_500,
      });
    }
    if (route.__vd_exactActive) {
      tags.push({
        label: 'exact',
        textColor: 0,
        backgroundColor: LIME_500,
      });
    }
    if (route.__vd_active) {
      tags.push({
        label: 'active',
        textColor: 0,
        backgroundColor: BLUE_600,
      });
    }
    if (record.redirect) {
      tags.push({
        label: 'redirect: ' +
            (typeof record.redirect === 'string' ? record.redirect : 'Object'),
        textColor: 0xffffff,
        backgroundColor: DARK,
      });
    }
    // add an id to be able to select it. Using the `path` is not possible because
    // empty path children would collide with their parents
    let id = record.__vd_id;
    if (id == null) {
      id = String(routeRecordId++);
      record.__vd_id = id;
    }
    return {
      id,
      label: record.path,
      tags,
      children: route.children.map(formatRouteRecordForInspector),
    };
  }
  //  incremental id for route records and inspector state
  let routeRecordId = 0;
  const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;
  function markRouteRecordActive(route, currentRoute) {
    // no route will be active if matched is empty
    // reset the matching state
    const isExactActive = currentRoute.matched.length &&
        isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
    route.__vd_exactActive = route.__vd_active = isExactActive;
    if (!isExactActive) {
      route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
    }
    route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
  }
  function resetMatchStateOnRouteRecord(route) {
    route.__vd_match = false;
    route.children.forEach(resetMatchStateOnRouteRecord);
  }
  function isRouteMatching(route, filter) {
    const found = String(route.re).match(EXTRACT_REGEXP_RE);
    route.__vd_match = false;
    if (!found || found.length < 3) {
      return false;
    }
    // use a regexp without $ at the end to match nested routes better
    const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]);
    if (nonEndingRE.test(filter)) {
      // mark children as matches
      route.children.forEach(child => isRouteMatching(child, filter));
      // exception case: `/`
      if (route.record.path !== '/' || filter === '/') {
        route.__vd_match = route.re.test(filter);
        return true;
      }
      // hide the / route
      return false;
    }
    const path = route.record.path.toLowerCase();
    const decodedPath = decode(path);
    // also allow partial matching on the path
    if (!filter.startsWith('/') &&
        (decodedPath.includes(filter) || path.includes(filter)))
      return true;
    if (decodedPath.startsWith(filter) || path.startsWith(filter))
      return true;
    if (route.record.name && String(route.record.name).includes(filter))
      return true;
    return route.children.some(child => isRouteMatching(child, filter));
  }
  function omit(obj, keys) {
    const ret = {};
    for (let key in obj) {
      if (!keys.includes(key)) {
        // @ts-expect-error
        ret[key] = obj[key];
      }
    }
    return ret;
  }

  /**
   * Creates a Router instance that can be used by a Vue app.
   *
   * @param options - {@link RouterOptions}
   */
  function createRouter(options) {
    const matcher = createRouterMatcher(options.routes, options);
    let parseQuery$1 = options.parseQuery || parseQuery;
    let stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
    let routerHistory = options.history;
    if (!routerHistory)
      throw new Error('Provide the "history" option when calling "createRouter()":' +
          ' https://next.router.vuejs.org/api/#history.');
    const beforeGuards = useCallbacks();
    const beforeResolveGuards = useCallbacks();
    const afterGuards = useCallbacks();
    const currentRoute = vue.shallowRef(START_LOCATION_NORMALIZED);
    let pendingLocation = START_LOCATION_NORMALIZED;
    // leave the scrollRestoration if no scrollBehavior is provided
    if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
      history.scrollRestoration = 'manual';
    }
    const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue);
    const encodeParams = applyToParams.bind(null, encodeParam);
    const decodeParams = applyToParams.bind(null, decode);
    function addRoute(parentOrRoute, route) {
      let parent;
      let record;
      if (isRouteName(parentOrRoute)) {
        parent = matcher.getRecordMatcher(parentOrRoute);
        record = route;
      }
      else {
        record = parentOrRoute;
      }
      return matcher.addRoute(record, parent);
    }
    function removeRoute(name) {
      let recordMatcher = matcher.getRecordMatcher(name);
      if (recordMatcher) {
        matcher.removeRoute(recordMatcher);
      }
      else {
        warn(`Cannot remove non-existent route "${String(name)}"`);
      }
    }
    function getRoutes() {
      return matcher.getRoutes().map(routeMatcher => routeMatcher.record);
    }
    function hasRoute(name) {
      return !!matcher.getRecordMatcher(name);
    }
    function resolve(rawLocation, currentLocation) {
      // const objectLocation = routerLocationAsObject(rawLocation)
      // we create a copy to modify it later
      currentLocation = assign({}, currentLocation || currentRoute.value);
      if (typeof rawLocation === 'string') {
        let locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
        let matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation);
        let href = routerHistory.createHref(locationNormalized.fullPath);
        {
          if (href.startsWith('//'))
            warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
          else if (!matchedRoute.matched.length) {
            warn(`No match found for location with path "${rawLocation}"`);
          }
        }
        // locationNormalized is always a new object
        return assign(locationNormalized, matchedRoute, {
          params: decodeParams(matchedRoute.params),
          hash: decode(locationNormalized.hash),
          redirectedFrom: undefined,
          href,
        });
      }
      let matcherLocation;
      // path could be relative in object as well
      if ('path' in rawLocation) {
        if ('params' in rawLocation &&
            !('name' in rawLocation) &&
            Object.keys(rawLocation.params).length) {
          warn(`Path "${rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
        }
        matcherLocation = assign({}, rawLocation, {
          path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path,
        });
      }
      else {
        // pass encoded values to the matcher so it can produce encoded path and fullPath
        matcherLocation = assign({}, rawLocation, {
          params: encodeParams(rawLocation.params),
        });
        // current location params are decoded, we need to encode them in case the
        // matcher merges the params
        currentLocation.params = encodeParams(currentLocation.params);
      }
      let matchedRoute = matcher.resolve(matcherLocation, currentLocation);
      const hash = rawLocation.hash || '';
      if (hash && !hash.startsWith('#')) {
        warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
      }
      // decoding them) the matcher might have merged current location params so
      // we need to run the decoding again
      matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
      const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
        hash: encodeHash(hash),
        path: matchedRoute.path,
      }));
      let href = routerHistory.createHref(fullPath);
      {
        if (href.startsWith('//')) {
          warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
        }
        else if (!matchedRoute.matched.length) {
          warn(`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`);
        }
      }
      return assign({
        fullPath,
        // keep the hash encoded so fullPath is effectively path + encodedQuery +
        // hash
        hash,
        query:
        // if the user is using a custom query lib like qs, we might have
        // nested objects, so we keep the query as is, meaning it can contain
        // numbers at `$route.query`, but at the point, the user will have to
        // use their own type anyway.
        // https://github.com/vuejs/vue-router-next/issues/328#issuecomment-649481567
            stringifyQuery$1 === stringifyQuery
                ? normalizeQuery(rawLocation.query)
                : rawLocation.query,
      }, matchedRoute, {
        redirectedFrom: undefined,
        href,
      });
    }
    function locationAsObject(to) {
      return typeof to === 'string'
          ? parseURL(parseQuery$1, to, currentRoute.value.path)
          : assign({}, to);
    }
    function checkCanceledNavigation(to, from) {
      if (pendingLocation !== to) {
        return createRouterError(8 /* NAVIGATION_CANCELLED */, {
          from,
          to,
        });
      }
    }
    function push(to) {
      return pushWithRedirect(to);
    }
    function replace(to) {
      return push(assign(locationAsObject(to), { replace: true }));
    }
    function handleRedirectRecord(to) {
      const lastMatched = to.matched[to.matched.length - 1];
      if (lastMatched && lastMatched.redirect) {
        const { redirect } = lastMatched;
        let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect;
        if (typeof newTargetLocation === 'string') {
          newTargetLocation =
              newTargetLocation.includes('?') || newTargetLocation.includes('#')
                  ? (newTargetLocation = locationAsObject(newTargetLocation))
                  : // force empty params
                  { path: newTargetLocation };
          // @ts-expect-error: force empty params when a string is passed to let
          // the router parse them again
          newTargetLocation.params = {};
        }
        if (!('path' in newTargetLocation) &&
            !('name' in newTargetLocation)) {
          warn(`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
          throw new Error('Invalid redirect');
        }
        return assign({
          query: to.query,
          hash: to.hash,
          params: to.params,
        }, newTargetLocation);
      }
    }
    function pushWithRedirect(to, redirectedFrom) {
      const targetLocation = (pendingLocation = resolve(to));
      const from = currentRoute.value;
      const data = to.state;
      const force = to.force;
      // to could be a string where `replace` is a function
      const replace = to.replace === true;
      const shouldRedirect = handleRedirectRecord(targetLocation);
      if (shouldRedirect)
        return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
              state: data,
              force,
              replace,
            }),
            // keep original redirectedFrom if it exists
            redirectedFrom || targetLocation);
      // if it was a redirect we already called `pushWithRedirect` above
      const toLocation = targetLocation;
      toLocation.redirectedFrom = redirectedFrom;
      let failure;
      if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
        failure = createRouterError(16 /* NAVIGATION_DUPLICATED */, { to: toLocation, from });
        // trigger scroll to allow scrolling to the same anchor
        handleScroll(from, from,
            // this is a push, the only way for it to be triggered from a
            // history.listen is with a redirect, which makes it become a push
            true,
            // This cannot be the first navigation because the initial location
            // cannot be manually navigated to
            false);
      }
      return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
          .catch((error) => isNavigationFailure(error)
              ? error
              : // reject any unknown error
              triggerError(error, toLocation, from))
          .then((failure) => {
            if (failure) {
              if (isNavigationFailure(failure, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
                if (// we are redirecting to the same location we were already at
                    isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
                    // and we have done it a couple of times
                    redirectedFrom &&
                    // @ts-expect-error: added only in dev
                    (redirectedFrom._count = redirectedFrom._count
                        ? // @ts-expect-error
                        redirectedFrom._count + 1
                        : 1) > 10) {
                  warn(`Detected an infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow. This will break in production if not fixed.`);
                  return Promise.reject(new Error('Infinite redirect in navigation guard'));
                }
                return pushWithRedirect(
                    // keep options
                    assign(locationAsObject(failure.to), {
                      state: data,
                      force,
                      replace,
                    }),
                    // preserve the original redirectedFrom if any
                    redirectedFrom || toLocation);
              }
            }
            else {
              // if we fail we don't finalize the navigation
              failure = finalizeNavigation(toLocation, from, true, replace, data);
            }
            triggerAfterEach(toLocation, from, failure);
            return failure;
          });
    }
    /**
     * Helper to reject and skip all navigation guards if a new navigation happened
     * @param to
     * @param from
     */
    function checkCanceledNavigationAndReject(to, from) {
      const error = checkCanceledNavigation(to, from);
      return error ? Promise.reject(error) : Promise.resolve();
    }
    // TODO: refactor the whole before guards by internally using router.beforeEach
    function navigate(to, from) {
      let guards;
      const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
      // all components here have been resolved once because we are leaving
      guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from);
      // leavingRecords is already reversed
      for (const record of leavingRecords) {
        record.leaveGuards.forEach(guard => {
          guards.push(guardToPromiseFn(guard, to, from));
        });
      }
      const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
      guards.push(canceledNavigationCheck);
      // run the queue of per route beforeRouteLeave guards
      return (runGuardQueue(guards)
          .then(() => {
            // check global guards beforeEach
            guards = [];
            for (const guard of beforeGuards.list()) {
              guards.push(guardToPromiseFn(guard, to, from));
            }
            guards.push(canceledNavigationCheck);
            return runGuardQueue(guards);
          })
          .then(() => {
            // check in components beforeRouteUpdate
            guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from);
            for (const record of updatingRecords) {
              record.updateGuards.forEach(guard => {
                guards.push(guardToPromiseFn(guard, to, from));
              });
            }
            guards.push(canceledNavigationCheck);
            // run the queue of per route beforeEnter guards
            return runGuardQueue(guards);
          })
          .then(() => {
            // check the route beforeEnter
            guards = [];
            for (const record of to.matched) {
              // do not trigger beforeEnter on reused views
              if (record.beforeEnter && !from.matched.includes(record)) {
                if (Array.isArray(record.beforeEnter)) {
                  for (const beforeEnter of record.beforeEnter)
                    guards.push(guardToPromiseFn(beforeEnter, to, from));
                }
                else {
                  guards.push(guardToPromiseFn(record.beforeEnter, to, from));
                }
              }
            }
            guards.push(canceledNavigationCheck);
            // run the queue of per route beforeEnter guards
            return runGuardQueue(guards);
          })
          .then(() => {
            // NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
            // clear existing enterCallbacks, these are added by extractComponentsGuards
            to.matched.forEach(record => (record.enterCallbacks = {}));
            // check in-component beforeRouteEnter
            guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from);
            guards.push(canceledNavigationCheck);
            // run the queue of per route beforeEnter guards
            return runGuardQueue(guards);
          })
          .then(() => {
            // check global guards beforeResolve
            guards = [];
            for (const guard of beforeResolveGuards.list()) {
              guards.push(guardToPromiseFn(guard, to, from));
            }
            guards.push(canceledNavigationCheck);
            return runGuardQueue(guards);
          })
          // catch any navigation canceled
          .catch(err => isNavigationFailure(err, 8 /* NAVIGATION_CANCELLED */)
              ? err
              : Promise.reject(err)));
    }
    function triggerAfterEach(to, from, failure) {
      // navigation is confirmed, call afterGuards
      // TODO: wrap with error handlers
      for (const guard of afterGuards.list())
        guard(to, from, failure);
    }
    /**
     * - Cleans up any navigation guards
     * - Changes the url if necessary
     * - Calls the scrollBehavior
     */
    function finalizeNavigation(toLocation, from, isPush, replace, data) {
      // a more recent navigation took place
      const error = checkCanceledNavigation(toLocation, from);
      if (error)
        return error;
      // only consider as push if it's not the first navigation
      const isFirstNavigation = from === START_LOCATION_NORMALIZED;
      const state = !isBrowser ? {} : history.state;
      // change URL only if the user did a push/replace and if it's not the initial navigation because
      // it's just reflecting the url
      if (isPush) {
        // on the initial navigation, we want to reuse the scroll position from
        // history state if it exists
        if (replace || isFirstNavigation)
          routerHistory.replace(toLocation.fullPath, assign({
            scroll: isFirstNavigation && state && state.scroll,
          }, data));
        else
          routerHistory.push(toLocation.fullPath, data);
      }
      // accept current navigation
      currentRoute.value = toLocation;
      handleScroll(toLocation, from, isPush, isFirstNavigation);
      markAsReady();
    }
    let removeHistoryListener;
    // attach listener to history to trigger navigations
    function setupListeners() {
      removeHistoryListener = routerHistory.listen((to, _from, info) => {
        // cannot be a redirect route because it was in history
        let toLocation = resolve(to);
        // due to dynamic routing, and to hash history with manual navigation
        // (manually changing the url or calling history.hash = '#/somewhere'),
        // there could be a redirect record in history
        const shouldRedirect = handleRedirectRecord(toLocation);
        if (shouldRedirect) {
          pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop);
          return;
        }
        pendingLocation = toLocation;
        const from = currentRoute.value;
        // TODO: should be moved to web history?
        if (isBrowser) {
          saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
        }
        navigate(toLocation, from)
            .catch((error) => {
              if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ | 8 /* NAVIGATION_CANCELLED */)) {
                return error;
              }
              if (isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
                // Here we could call if (info.delta) routerHistory.go(-info.delta,
                // false) but this is bug prone as we have no way to wait the
                // navigation to be finished before calling pushWithRedirect. Using
                // a setTimeout of 16ms seems to work but there is not guarantee for
                // it to work on every browser. So Instead we do not restore the
                // history entry and trigger a new navigation as requested by the
                // navigation guard.
                // the error is already handled by router.push we just want to avoid
                // logging the error
                pushWithRedirect(error.to, toLocation
                    // avoid an uncaught rejection, let push call triggerError
                )
                    .then(failure => {
                      // manual change in hash history #916 ending up in the URL not
                      // changing but it was changed by the manual url change, so we
                      // need to manually change it ourselves
                      if (isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ |
                          16 /* NAVIGATION_DUPLICATED */) &&
                          !info.delta &&
                          info.type === NavigationType.pop) {
                        routerHistory.go(-1, false);
                      }
                    })
                    .catch(noop);
                // avoid the then branch
                return Promise.reject();
              }
              // do not restore history on unknown direction
              if (info.delta)
                routerHistory.go(-info.delta, false);
              // unrecognized error, transfer to the global handler
              return triggerError(error, toLocation, from);
            })
            .then((failure) => {
              failure =
                  failure ||
                  finalizeNavigation(
                      // after navigation, all matched components are resolved
                      toLocation, from, false);
              // revert the navigation
              if (failure) {
                if (info.delta) {
                  routerHistory.go(-info.delta, false);
                }
                else if (info.type === NavigationType.pop &&
                    isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ | 16 /* NAVIGATION_DUPLICATED */)) {
                  // manual change in hash history #916
                  // it's like a push but lacks the information of the direction
                  routerHistory.go(-1, false);
                }
              }
              triggerAfterEach(toLocation, from, failure);
            })
            .catch(noop);
      });
    }
    // Initialization and Errors
    let readyHandlers = useCallbacks();
    let errorHandlers = useCallbacks();
    let ready;
    /**
     * Trigger errorHandlers added via onError and throws the error as well
     *
     * @param error - error to throw
     * @param to - location we were navigating to when the error happened
     * @param from - location we were navigating from when the error happened
     * @returns the error as a rejected promise
     */
    function triggerError(error, to, from) {
      markAsReady(error);
      const list = errorHandlers.list();
      if (list.length) {
        list.forEach(handler => handler(error, to, from));
      }
      else {
        {
          warn('uncaught error during route navigation:');
        }
        console.error(error);
      }
      return Promise.reject(error);
    }
    function isReady() {
      if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
        return Promise.resolve();
      return new Promise((resolve, reject) => {
        readyHandlers.add([resolve, reject]);
      });
    }
    /**
     * Mark the router as ready, resolving the promised returned by isReady(). Can
     * only be called once, otherwise does nothing.
     * @param err - optional error
     */
    function markAsReady(err) {
      if (ready)
        return;
      ready = true;
      setupListeners();
      readyHandlers
          .list()
          .forEach(([resolve, reject]) => (err ? reject(err) : resolve()));
      readyHandlers.reset();
    }
    // Scroll behavior
    function handleScroll(to, from, isPush, isFirstNavigation) {
      const { scrollBehavior } = options;
      if (!isBrowser || !scrollBehavior)
        return Promise.resolve();
      let scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
          ((isFirstNavigation || !isPush) &&
              history.state &&
              history.state.scroll) ||
          null;
      return vue.nextTick()
          .then(() => scrollBehavior(to, from, scrollPosition))
          .then(position => position && scrollToPosition(position))
          .catch(err => triggerError(err, to, from));
    }
    const go = (delta) => routerHistory.go(delta);
    let started;
    const installedApps = new Set();
    const router = {
      currentRoute,
      addRoute,
      removeRoute,
      hasRoute,
      getRoutes,
      resolve,
      options,
      push,
      replace,
      go,
      back: () => go(-1),
      forward: () => go(1),
      beforeEach: beforeGuards.add,
      beforeResolve: beforeResolveGuards.add,
      afterEach: afterGuards.add,
      onError: errorHandlers.add,
      isReady,
      install(app) {
        const router = this;
        app.component('RouterLink', RouterLink);
        app.component('RouterView', RouterView);
        app.config.globalProperties.$router = router;
        Object.defineProperty(app.config.globalProperties, '$route', {
          enumerable: true,
          get: () => vue.unref(currentRoute),
        });
        // this initial navigation is only necessary on client, on server it doesn't
        // make sense because it will create an extra unnecessary navigation and could
        // lead to problems
        if (isBrowser &&
            // used for the initial navigation client side to avoid pushing
            // multiple times when the router is used in multiple apps
            !started &&
            currentRoute.value === START_LOCATION_NORMALIZED) {
          // see above
          started = true;
          push(routerHistory.location).catch(err => {
            warn('Unexpected error when starting the router:', err);
          });
        }
        const reactiveRoute = {};
        for (let key in START_LOCATION_NORMALIZED) {
          // @ts-expect-error: the key matches
          reactiveRoute[key] = vue.computed(() => currentRoute.value[key]);
        }
        app.provide(routerKey, router);
        app.provide(routeLocationKey, vue.reactive(reactiveRoute));
        app.provide(routerViewLocationKey, currentRoute);
        let unmountApp = app.unmount;
        installedApps.add(app);
        app.unmount = function () {
          installedApps.delete(app);
          if (installedApps.size < 1) {
            removeHistoryListener();
            currentRoute.value = START_LOCATION_NORMALIZED;
            started = false;
            ready = false;
          }
          unmountApp();
        };
        if (isBrowser) {
          addDevtools(app, router, matcher);
        }
      },
    };
    return router;
  }
  function runGuardQueue(guards) {
    return guards.reduce((promise, guard) => promise.then(() => guard()), Promise.resolve());
  }
  function extractChangingRecords(to, from) {
    const leavingRecords = [];
    const updatingRecords = [];
    const enteringRecords = [];
    const len = Math.max(from.matched.length, to.matched.length);
    for (let i = 0; i < len; i++) {
      const recordFrom = from.matched[i];
      if (recordFrom) {
        if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
          updatingRecords.push(recordFrom);
        else
          leavingRecords.push(recordFrom);
      }
      const recordTo = to.matched[i];
      if (recordTo) {
        // the type doesn't matter because we are comparing per reference
        if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
          enteringRecords.push(recordTo);
        }
      }
    }
    return [leavingRecords, updatingRecords, enteringRecords];
  }

  /**
   * Returns the router instance. Equivalent to using `$router` inside
   * templates.
   */
  function useRouter() {
    return vue.inject(routerKey);
  }
  /**
   * Returns the current route location. Equivalent to using `$route` inside
   * templates.
   */
  function useRoute() {
    return vue.inject(routeLocationKey);
  }

  exports.RouterLink = RouterLink;
  exports.RouterView = RouterView;
  exports.START_LOCATION = START_LOCATION_NORMALIZED;
  exports.createMemoryHistory = createMemoryHistory;
  exports.createRouter = createRouter;
  exports.createRouterMatcher = createRouterMatcher;
  exports.createWebHashHistory = createWebHashHistory;
  exports.createWebHistory = createWebHistory;
  exports.isNavigationFailure = isNavigationFailure;
  exports.matchedRouteKey = matchedRouteKey;
  exports.onBeforeRouteLeave = onBeforeRouteLeave;
  exports.onBeforeRouteUpdate = onBeforeRouteUpdate;
  exports.parseQuery = parseQuery;
  exports.routeLocationKey = routeLocationKey;
  exports.routerKey = routerKey;
  exports.routerViewLocationKey = routerViewLocationKey;
  exports.stringifyQuery = stringifyQuery;
  exports.useLink = useLink;
  exports.useRoute = useRoute;
  exports.useRouter = useRouter;
  exports.viewDepthKey = viewDepthKey;

  Object.defineProperty(exports, '__esModule', { value: true });

  return exports;

}({}, Vue));